// 第十步：引入utils.js  里面一个isPlainObject里面的这个方法检测是不是一个纯对象 isPlainObject: yi si pu lei 
import _ from '../assets/utils';

/* 第一步：实现redux的部分源码 */
// createStore:ke rui te si duo
// 第二步：首先呢我得创建一个createStore我要执行这个方法的时候里面传入一个reducer
export const createStore = function createStore(reducer) {
    // 第十三步：判断reducer是不是一个函数如果不是函数返回错误
    if (typeof reducer !== 'function') throw new Error("Expected the root reducer to be a function");

    // 第五步：创建一个state初始状态是undefined
    let state, // 存放公共状态
        listeners = []; // 事件池 可以把组件更新的方法放到事件池中 

    // 第四步：创建这三个方法 getState获取公共状态 、 subscribe向事件池中加入让组件更新的方法 、 dispatch派发任务通知reducer执行
    /* 获取公共状态 */
    const getState = function getState() {
        // 第六步：当我们执行getState的时候怎么办返回公共状态信息即可
        return state;
    };

    /* 向事件池中加入让组件更新的方法 */ // 执行subscribe的时候里面传入一个listener函数进来 
    const subscribe = function subscribe(listener) {
        // 规则校验
        // 第七步：当我们传递过来的listener是一个函数如果不是一个函数该怎么办我可以做一个报错
        if (typeof listener !== "function") throw new TypeError("Expected the listener to be a function");
        // 把传入的方法(让组件更新的办法)加入到事件池中[需要做去重处理]
        /** 第八步：接下来我们要做的就是把我传递过来的函数能放到我们事件池中就可以了，怎么往事件池里边放呢往里面放的时候呢我得先判断这个方法
          * 存不存在,如果已经存在这个方法了就不再放了，放之前呢做一个去重
          */
        // 判断listeners这个集合检查我们listener 如果没有的话我们就把listener存进来，有的话就没必要存了
        if (!listeners.includes(listener)) { // listener: yin ku lu si
            listeners.push(listener);
        }
        // 第九步：返回一个从事件池中移出方法的函数
        return function unsubscribe() {
            // 获得listener在当前集合的索引
            let index = listeners.indexOf(listener);
            // 从当前位置移出掉就行了
            listeners.splice(index, 1);
        };
    };

    /* 派发任务通知reducer执行 */  // 执行dispatch的时候里面要传入一个action对象
    const dispatch = function dispatch(action) {
        // 规则校验
        // 第十一步：如果_.isPlainObject检测我们是不是一个对象 如果不是一个对象抛出一个异常错误
        if (!_.isPlainObject(action)) throw new TypeError("Actions must be plain objects");
        // 如果没有这个属性就返回异常错误
        if (typeof action.type === "undefined") throw new TypeError("Actions may not have an undefined 'type' property");

        // 第十二步：dispatch派发的话目的是谁 ，就是把我一开始的传入过来的reducer执行
        // 把reducer执行，传递：公共状态、行为对象;  每一次dispatch派发都会把reducer执行， 接收执行的返回值，替换公共状态;
        // 第十四步：如果reducer是一个函数 dispatch派发的时候怎么办就把传递过来的reducer执行，当执行reducer的时候怎么办要把state传过来，
        // 如果action如果没有的话是一个undefined 那这个时候state就会走我们的初始状态值，action就是我们传递过来的action，
        state = reducer(state, action);

        // 第十五步：当状态更改，我们还需要把事件池中的方法执行
        listeners.forEach(listener => {
            // listener就是让组件更新的方法
            listener()
        });
        // 最后会返回一个action
        return action;
    };

    /* redux内部会默认进行一次dispatch派发，目的：给公共容器中的状态赋值初始值 */
    const randomString = () => Math.random().toString(36).substring(7).split('').join('.');

    dispatch({
        // type: Symbol()
        type: "@@redux/INIT" + randomString()
    });

    // 第三步：返回创建的store对象 对象里面是有三个方法
    return {
        getState,
        subscribe,
        dispatch
    }
};